import tkinter as tk
from tkinter import ttk, scrolledtext, filedialog, messagebox
import winrm
import base64
import threading
import os
from datetime import datetime


class WinRMManager:
    def __init__(self, root):
        self.root = root
        self.root.title("Windows远程管理工具")
        self.root.geometry("900x700")
        self.root.minsize(800, 600)

        # 设置中文字体支持
        self.root.option_add("*Font", "SimHei 10")

        # 服务器连接信息
        self.server_data = {
            "host": tk.StringVar(value="ip"),
            "port": tk.StringVar(value="5985"),
            "username": tk.StringVar(value=""),
            "password": tk.StringVar(value=""),
            "auth_method": tk.StringVar(value="ntlm")
        }

        # 连接状态
        self.connected = False
        self.session = None

        # 创建界面
        self.create_widgets()

    def create_widgets(self):
        # 主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 服务器连接部分
        conn_frame = ttk.LabelFrame(main_frame, text="服务器连接", padding="10")
        conn_frame.pack(fill=tk.X, pady=(0, 10))

        # 左侧连接信息
        left_conn_frame = ttk.Frame(conn_frame)
        left_conn_frame.pack(side=tk.LEFT, fill=tk.X, expand=True)

        ttk.Label(left_conn_frame, text="主机:").grid(row=0, column=0, sticky=tk.W, pady=5)
        ttk.Entry(left_conn_frame, textvariable=self.server_data["host"]).grid(row=0, column=1, sticky=(tk.W, tk.E),
                                                                               pady=5)

        ttk.Label(left_conn_frame, text="端口:").grid(row=0, column=2, sticky=tk.W, pady=5)
        ttk.Entry(left_conn_frame, textvariable=self.server_data["port"], width=8).grid(row=0, column=3, sticky=tk.W,
                                                                                        pady=5)

        ttk.Label(left_conn_frame, text="用户名:").grid(row=1, column=0, sticky=tk.W, pady=5)
        ttk.Entry(left_conn_frame, textvariable=self.server_data["username"]).grid(row=1, column=1, sticky=(tk.W, tk.E),
                                                                                   pady=5)

        ttk.Label(left_conn_frame, text="密码:").grid(row=1, column=2, sticky=tk.W, pady=5)
        ttk.Entry(left_conn_frame, textvariable=self.server_data["password"], show="*").grid(row=1, column=3,
                                                                                             sticky=tk.W, pady=5)

        # 右侧连接按钮
        right_conn_frame = ttk.Frame(conn_frame)
        right_conn_frame.pack(side=tk.RIGHT, padx=(10, 0))

        self.connect_btn = ttk.Button(right_conn_frame, text="连接", command=self.toggle_connection)
        self.connect_btn.pack(padx=5)

        # 状态显示
        self.status_var = tk.StringVar(value="未连接")
        ttk.Label(conn_frame, textvariable=self.status_var).pack(side=tk.RIGHT, padx=10)

        # 命令执行部分
        cmd_frame = ttk.LabelFrame(main_frame, text="命令执行", padding="10")
        cmd_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 10))

        ttk.Label(cmd_frame, text="输入命令:").pack(anchor=tk.W)

        cmd_entry_frame = ttk.Frame(cmd_frame)
        cmd_entry_frame.pack(fill=tk.X, pady=(0, 10))

        self.cmd_entry = ttk.Entry(cmd_entry_frame)
        self.cmd_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5))
        self.cmd_entry.bind('<Return>', lambda event: self.execute_command())

        ttk.Button(cmd_entry_frame, text="执行", command=self.execute_command).pack(side=tk.LEFT)

        # 常用命令按钮
        common_cmd_frame = ttk.Frame(cmd_frame)
        common_cmd_frame.pack(fill=tk.X, pady=(0, 10))

        common_cmds = ["ipconfig /all", "dir C:\\", "systeminfo", "tasklist", "whoami", "netstat -an"]
        for cmd in common_cmds:
            ttk.Button(common_cmd_frame, text=cmd, width=15,
                       command=lambda c=cmd: self.cmd_entry.insert(tk.END, c)).pack(side=tk.LEFT, padx=2, pady=2)

        # 结果显示区域
        ttk.Label(cmd_frame, text="执行结果:").pack(anchor=tk.W)
        self.result_area = scrolledtext.ScrolledText(cmd_frame, wrap=tk.WORD, height=10)
        self.result_area.pack(fill=tk.BOTH, expand=True)

        # 文件传输部分
        file_frame = ttk.LabelFrame(main_frame, text="文件传输", padding="10")
        file_frame.pack(fill=tk.BOTH, expand=True)

        # 上传文件
        upload_frame = ttk.Frame(file_frame)
        upload_frame.pack(fill=tk.X, pady=(0, 10))

        ttk.Label(upload_frame, text="上传文件:").pack(side=tk.LEFT, padx=(0, 5))

        self.local_file_var = tk.StringVar()
        ttk.Entry(upload_frame, textvariable=self.local_file_var, width=50).pack(side=tk.LEFT, padx=(0, 5))

        ttk.Button(upload_frame, text="浏览...", command=self.browse_local_file).pack(side=tk.LEFT, padx=(0, 5))

        self.remote_path_var = tk.StringVar(value="C:\\temp\\")
        ttk.Entry(upload_frame, textvariable=self.remote_path_var, width=20).pack(side=tk.LEFT, padx=(0, 5))

        ttk.Button(upload_frame, text="上传", command=self.upload_file).pack(side=tk.LEFT)

        # 下载文件
        download_frame = ttk.Frame(file_frame)
        download_frame.pack(fill=tk.X, pady=(0, 10))

        ttk.Label(download_frame, text="下载文件:").pack(side=tk.LEFT, padx=(0, 5))

        self.remote_file_var = tk.StringVar(value="C:\\temp\\")
        ttk.Entry(download_frame, textvariable=self.remote_file_var, width=50).pack(side=tk.LEFT, padx=(0, 5))

        self.local_path_var = tk.StringVar()
        ttk.Entry(download_frame, textvariable=self.local_path_var, width=20).pack(side=tk.LEFT, padx=(0, 5))

        ttk.Button(download_frame, text="浏览...", command=self.browse_local_path).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(download_frame, text="下载", command=self.download_file).pack(side=tk.LEFT)

        # 状态栏
        status_frame = ttk.Frame(self.root)
        status_frame.pack(side=tk.BOTTOM, fill=tk.X)

        self.status_bar = ttk.Label(status_frame, text="就绪", relief=tk.SUNKEN, anchor=tk.W)
        self.status_bar.pack(fill=tk.X)

    def toggle_connection(self):
        """切换连接状态"""
        if self.connected:
            # 断开连接
            self.disconnect()
        else:
            # 连接服务器
            self.connect()

    def connect(self):
        """连接到远程服务器"""
        self.status_bar.config(text="正在连接...")
        self.connect_btn.config(state=tk.DISABLED)

        # 在新线程中执行连接，避免界面卡顿
        threading.Thread(target=self._connect_thread, daemon=True).start()

    def _connect_thread(self):
        """连接线程函数"""
        try:
            host = self.server_data["host"].get()
            port = int(self.server_data["port"].get())
            username = self.server_data["username"].get()
            password = self.server_data["password"].get()
            auth_method = self.server_data["auth_method"].get()

            endpoint = f'http://{host}:{port}/wsman'

            self.session = winrm.Session(
                endpoint,
                auth=(username, password),
                transport=auth_method,
                server_cert_validation='ignore'
            )

            # 测试连接
            result = self.session.run_cmd('echo', ['Connected'])

            if result.status_code == 0:
                self.connected = True
                self.root.after(0, self._update_connection_status, True)
            else:
                self.root.after(0, self._update_connection_status, False, "命令执行失败")

        except Exception as e:
            self.root.after(0, self._update_connection_status, False, str(e))

    def _update_connection_status(self, success, error_msg=None):
        """更新连接状态UI"""
        if success:
            self.status_var.set("已连接")
            self.connect_btn.config(text="断开", state=tk.NORMAL)
            self.status_bar.config(text=f"已连接到 {self.server_data['host'].get()}")
            self.result_area.insert(tk.END, f"已成功连接到 {self.server_data['host'].get()}\n")
        else:
            self.status_var.set("未连接")
            self.connect_btn.config(text="连接", state=tk.NORMAL)
            self.status_bar.config(text=f"连接失败: {error_msg}")
            self.result_area.insert(tk.END, f"连接失败: {error_msg}\n")

        self.result_area.see(tk.END)

    def disconnect(self):
        """断开连接"""
        self.connected = False
        self.session = None
        self.status_var.set("未连接")
        self.connect_btn.config(text="连接")
        self.status_bar.config(text="已断开连接")
        self.result_area.insert(tk.END, "已断开连接\n")
        self.result_area.see(tk.END)

    def execute_command(self):
        """执行命令"""
        if not self.connected:
            messagebox.showinfo("提示", "请先连接服务器")
            return

        command = self.cmd_entry.get().strip()
        if not command:
            return

        self.result_area.insert(tk.END, f"执行命令: {command}\n")
        self.result_area.insert(tk.END, "-" * 50 + "\n")

        # 在新线程中执行命令
        threading.Thread(target=self._execute_command_thread, args=(command,), daemon=True).start()

    def _execute_command_thread(self, command):
        """命令执行线程函数"""
        try:
            result = self.session.run_cmd(command)

            # 更新UI显示结果
            self.root.after(0, self._update_command_result, command, result)
        except Exception as e:
            # 更新UI显示错误
            self.root.after(0, self._update_command_result, command, None, str(e))

    def _update_command_result(self, command, result, error=None):
        """更新命令执行结果UI"""
        if error:
            self.result_area.insert(tk.END, f"错误: {error}\n")
        else:
            if result.std_out:
                self.result_area.insert(tk.END, result.std_out.decode('utf-8', errors='ignore'))

            if result.std_err:
                self.result_area.insert(tk.END, f"错误: {result.std_err.decode('utf-8', errors='ignore')}\n")

            self.result_area.insert(tk.END, f"\n命令 '{command}' 执行完毕，状态码: {result.status_code}\n")

        self.result_area.insert(tk.END, "=" * 50 + "\n")
        self.result_area.see(tk.END)
        self.status_bar.config(text="命令执行完毕")

    def browse_local_file(self):
        """浏览本地文件"""
        filename = filedialog.askopenfilename()
        if filename:
            self.local_file_var.set(filename)
            # 自动填充远程路径
            remote_dir = self.remote_path_var.get()
            if not remote_dir.endswith("\\"):
                remote_dir += "\\"
            self.remote_path_var.set(remote_dir + os.path.basename(filename))

    def browse_local_path(self):
        """浏览本地保存路径"""
        filename = filedialog.asksaveasfilename()
        if filename:
            self.local_path_var.set(filename)

    def upload_file(self):
        """上传文件"""
        if not self.connected:
            messagebox.showinfo("提示", "请先连接服务器")
            return

        local_path = self.local_file_var.get()
        remote_path = self.remote_path_var.get()

        if not local_path or not remote_path:
            messagebox.showinfo("提示", "请选择本地文件和远程路径")
            return

        # 显示上传中状态
        self.status_bar.config(text=f"正在上传 {local_path} 到 {remote_path}...")

        # 在新线程中执行上传
        threading.Thread(target=self._upload_file_thread, args=(local_path, remote_path), daemon=True).start()

    def _upload_file_thread(self, local_path, remote_path):
        """文件上传线程函数"""
        try:
            # 读取本地文件内容
            with open(local_path, 'rb') as file:
                file_content = file.read()

            # Base64编码文件内容
            encoded_content = base64.b64encode(file_content).decode('utf-8')

            # 构建PowerShell命令写入文件
            ps_command = f"""
            $content = [System.Convert]::FromBase64String('{encoded_content}')
            [System.IO.File]::WriteAllBytes('{remote_path}', $content)
            """

            # 执行命令
            result = self.session.run_ps(ps_command)

            # 更新UI
            self.root.after(0, self._update_upload_result, local_path, remote_path, result.status_code, result.std_err)

        except Exception as e:
            self.root.after(0, self._update_upload_result, local_path, remote_path, 1, str(e).encode())

    def _update_upload_result(self, local_path, remote_path, status_code, error):
        """更新上传结果UI"""
        if status_code == 0:
            self.status_bar.config(text=f"上传成功: {local_path} -> {remote_path}")
            self.result_area.insert(tk.END, f"文件上传成功: {local_path} -> {remote_path}\n")
        else:
            error_msg = error.decode('utf-8', errors='ignore') if error else "未知错误"
            self.status_bar.config(text=f"上传失败: {error_msg}")
            self.result_area.insert(tk.END, f"文件上传失败: {error_msg}\n")

        self.result_area.see(tk.END)

    def download_file(self):
        """下载文件"""
        if not self.connected:
            messagebox.showinfo("提示", "请先连接服务器")
            return

        remote_path = self.remote_file_var.get()
        local_path = self.local_path_var.get()

        if not remote_path or not local_path:
            messagebox.showinfo("提示", "请输入远程文件路径和本地保存路径")
            return

        # 显示下载中状态
        self.status_bar.config(text=f"正在下载 {remote_path} 到 {local_path}...")

        # 在新线程中执行下载
        threading.Thread(target=self._download_file_thread, args=(remote_path, local_path), daemon=True).start()

    def _download_file_thread(self, remote_path, local_path):
        """文件下载线程函数"""
        try:
            # 构建PowerShell命令读取文件并Base64编码
            ps_command = f"""
            if (Test-Path '{remote_path}') {{
                $content = [System.IO.File]::ReadAllBytes('{remote_path}')
                [System.Convert]::ToBase64String($content)
            }} else {{
                "FILE_NOT_FOUND"
            }}
            """

            # 执行命令
            result = self.session.run_ps(ps_command)

            # 更新UI
            self.root.after(0, self._update_download_result, remote_path, local_path, result)

        except Exception as e:
            self.root.after(0, self._update_download_result, remote_path, local_path, None, str(e))

    def _update_download_result(self, remote_path, local_path, result, error=None):
        """更新下载结果UI"""
        if error:
            self.status_bar.config(text=f"下载失败: {error}")
            self.result_area.insert(tk.END, f"文件下载失败: {error}\n")
            return

        if result.status_code != 0:
            error_msg = result.std_err.decode('utf-8', errors='ignore') if result.std_err else "未知错误"
            self.status_bar.config(text=f"下载失败: {error_msg}")
            self.result_area.insert(tk.END, f"文件下载失败: {error_msg}\n")
            return

        output = result.std_out.decode('utf-8', errors='ignore').strip()

        if output == "FILE_NOT_FOUND":
            self.status_bar.config(text=f"下载失败: 文件不存在")
            self.result_area.insert(tk.END, f"文件下载失败: {remote_path} 不存在\n")
            return

        try:
            # 解码Base64内容并写入本地文件
            file_content = base64.b64decode(output)
            with open(local_path, 'wb') as file:
                file.write(file_content)

            self.status_bar.config(text=f"下载成功: {remote_path} -> {local_path}")
            self.result_area.insert(tk.END, f"文件下载成功: {remote_path} -> {local_path}\n")
        except Exception as e:
            self.status_bar.config(text=f"下载失败: 写入文件时出错 - {str(e)}")
            self.result_area.insert(tk.END, f"文件下载失败: 写入文件时出错 - {str(e)}\n")

        self.result_area.see(tk.END)


if __name__ == "__main__":
    root = tk.Tk()
    app = WinRMManager(root)
    root.mainloop()